/*
 * File     : PartialLibraryBuilder.java
 * Created  : 14 Oct 2011
 *
 * Copyright © 2011 Matthew Wilson (mj. {my-surname} .uk {at} gmail.com)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package com.googlecode.dni.internal.library;

import java.lang.reflect.Method;
import java.util.List;

import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.MethodVisitor;

import com.googlecode.dni.internal.call.JniNativeMethod;
import com.googlecode.dni.internal.structure.StructureMethodsRecord;
import com.googlecode.dni.library.CallingConvention;
import com.googlecode.dni.library.Function;
import com.googlecode.dni.library.IllegalLibraryException;
import com.googlecode.dni.library.Library;
import com.googlecode.dni.type.structure.Structure;

/**
 * <p>
 *  Generates byte-code for implementations of partial library types for
 *  {@link Structure}s.
 * </p>
 *
 * @author Matthew Wilson
 */
public final class PartialLibraryBuilder extends AbstractLibraryBuilder
{

    private final StructureMethodsRecord record;

    /**
     * Creates a new instance.
     *
     * @param record
     *            the structure methods record
     * @param implName
     *            the structure impl name
     *
     * @return an instance
     */
    public static PartialLibraryBuilder create( final StructureMethodsRecord record,
                                                final String implName )
    {
        List< Method > structureMethods = record.getMethods();
        if ( structureMethods.isEmpty() )
        {
            throw new IllegalArgumentException();
        }

        Class< ? > structureType = record.getStructureType();

        Library library = record.getLibrary();
        CallingConvention conventions = library.callingConvention();
        if ( !conventions.isSupported() )
        {
            throw new IllegalLibraryException( "Unsupported calling conventions: "
                                               + structureType.getName() + ": " + conventions );
        }
        conventions = conventions.getActual();

        return new PartialLibraryBuilder( record,
                                          implName,
                                          library,
                                          conventions,
                                          library.charset() );
    }

    private PartialLibraryBuilder( final StructureMethodsRecord record,
                                   final String implName,
                                   final Library library,
                                   final CallingConvention conventions,
                                   final String charset )
    {
        super( record.getStructureType(),
               implName,
               library,
               conventions,
               charset );
        this.record = record;
    }

    /**
     * Builds the methods.
     *
     * @param classWriter
     *            the class writer
     * @param nativeMethods
     *            container to receive created native methods (remember to
     *            register them all!)
     */
    public void build( final ClassWriter classWriter,
                       final List< JniNativeMethod > nativeMethods )
    {
        buildInternal( classWriter, true, nativeMethods );
    }

    /**
     * Writes the static initialiser fragment.
     *
     * @param methodVisitor
     *            the method visitor
     */
    public void writeStaticInitialiserFragment( final MethodVisitor methodVisitor )
    {
        writeStaticInitialiserFragmentInternal( methodVisitor );
    }


    @Override
    void createMethods( final DynamicLibrary dynamicLibrary,
                        final ClassWriter classWriter,
                        final List< JniNativeMethod > nativeMethods )
    {
        for ( Method method : this.record.getMethods() )
        {
            if ( method.getDeclaringClass() != Object.class
                 && method.getAnnotation( Function.class ) != null )
            {
                try
                {
                    writeFunctionMethod( dynamicLibrary, method, nativeMethods );
                }
                catch ( Throwable throwable )
                {
                    throw new IllegalLibraryException( "Problem with method: " + method, throwable );
                }
            }
        }
    }

}
